<template>
    <Mapbox :mapOnLoadCB="mapOnLoadCB"></Mapbox>
</template>

<script>
import {ref} from "vue"
import Mapbox from "@/components/common/Mapbox.component.vue"
import MapboxCommonService from "@/service/map/MapboxCommonService"
import MapboxAssetsService from "@/service/http/MapboxAssets.service"

export default {
    name: "AddAnimateBuilding.component",
    components: {Mapbox},
    setup() {

        let mapStore = ref({});
        let mapOnLoadCB = async (map) => {
            mapStore.value = map;

            let {data} = await MapboxAssetsService.queryStyle("style-dancing-buildings.json")

            // 将运行的样式API（styling API）与网络音频API（Web Audio API）关联，创建一幅3D建筑物可随周边环境的节奏起舞的地图。
            map.addControl(new mapboxgl.FullscreenControl());
            map.setStyle(data)
            MapboxCommonService.setCZBP(map, [-74.00649562332922, 40.70811328605049], 15, 0, 55)

            setTimeout(() => {
                let bins = 16;
                let maxHeight = 200;
                let binWidth = maxHeight / bins;

                // Divide the buildings into 16 bins based on their true height, using a layer filter.
                for (let i = 0; i < bins; i++) {
                    map.addLayer({
                        'id': '3d-buildings-' + i,
                        'source': 'composite',
                        'source-layer': 'building',
                        'filter': ['all', ['==', 'extrude', 'true'], ['>', 'height', i * binWidth], ['<=', 'height', (i + 1) * binWidth]],
                        'type': 'fill-extrusion',
                        'minzoom': 15,
                        'paint': {
                            'fill-extrusion-color': '#aaa',
                            'fill-extrusion-height-transition': {
                                duration: 0,
                                delay: 0
                            },
                            'fill-extrusion-opacity': .6
                        }
                    });
                }

                // Older browsers might not implement mediaDevices at all, so we set an empty object first
                if (navigator.mediaDevices === undefined) {
                    navigator.mediaDevices = {};
                }

                // Some browsers partially implement mediaDevices. We can't just assign an object
                // with getUserMedia as it would overwrite existing properties.
                // Here, we will just add the getUserMedia property if it's missing.
                if (navigator.mediaDevices.getUserMedia === undefined) {
                    navigator.mediaDevices.getUserMedia = (constraints) => {

                        // First get ahold of the legacy getUserMedia, if present
                        let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

                        // Some browsers just don't implement it - return a rejected promise with an error
                        // to keep a consistent interface
                        if (!getUserMedia) {
                            return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
                        }

                        // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
                        return new Promise((resolve, reject) => {
                            getUserMedia.call(navigator, constraints, resolve, reject);
                        });
                    };
                }

                navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
                    // Set up a Web Audio AudioContext and AnalyzerNode, configured to return the
                    // same number of bins of audio frequency data.
                    let audioCtx = new (window.AudioContext || window.webkitAudioContext)();

                    let analyser = audioCtx.createAnalyser();
                    analyser.minDecibels = -90;
                    analyser.maxDecibels = -10;
                    analyser.smoothingTimeConstant = 0.85;

                    let source = audioCtx.createMediaStreamSource(stream);
                    source.connect(analyser);

                    analyser.fftSize = bins * 2;

                    let dataArray = new Uint8Array(bins);

                    const draw = (now) => {
                        analyser.getByteFrequencyData(dataArray);

                        // Use that data to drive updates to the fill-extrusion-height property.
                        let avg = 0;
                        for (let i = 0; i < bins; i++) {
                            avg += dataArray[i];
                            map.setPaintProperty('3d-buildings-' + i, 'fill-extrusion-height', 10 + 4 * i + dataArray[i]);
                        }
                        avg /= bins;

                        // Animate the map bearing and light color over time, and make the light more
                        // intense when the audio is louder.
                        map.setBearing(now / 500);
                        map.setLight({
                            color: "hsl(" + now / 100 % 360 + "," + Math.min(50 + avg / 4, 100) + "%,50%)",
                            intensity: Math.min(1, avg / 256 * 10)
                        });

                        requestAnimationFrame(draw);
                    }

                    requestAnimationFrame(draw);
                }).catch((err) => {
                    console.log('The following gUM error occured: ' + err);
                });
            }, 1000)
        };

        return {
            mapOnLoadCB
        }
    }
}
</script>

<style scoped>

</style>